package cn.anecansaitin.whimsy.util;

import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import org.joml.Quaternionf;
import org.joml.Vector3f;

import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

public final class HitResultUtil {
    private static final Predicate<Entity> PREDICATE = entity -> !entity.isSpectator() && entity.isPickable();

    public static Set<Entity> fanShapedRangeEntity(Entity shooter, float radius, float angle, int count) {
        if (count % 2 == 0) {
            count++;
        }

        EntityHitResult[] results = new EntityHitResult[count];
        int j = count / 2 + 1;
        float interval = angle / count - 1;

        for (int i = 1; i < j; i++) {
            EntityHitResult result1 = shootAtAngle(shooter, interval * i, radius);
            results[j - 1 - i] = result1;

            EntityHitResult result2 = shootAtAngle(shooter, -(interval * i), radius);
            results[j - 1 + i] = result2;
        }

        EntityHitResult result = shootAtAngle(shooter, angle, radius);
        results[j - 1] = result;

        HashSet<Entity> entities = new HashSet<>();

        for (EntityHitResult hitResult : results) {
            if (hitResult != null) {
                entities.add(hitResult.getEntity());
            }
        }

        return entities;
    }

    public static Set<Entity> lowFlyingFanShapedRangeEntity(Entity attacker, float radius, float angle, int count) {
        HashSet<Entity> ignoredEntities = new HashSet<>();

        if (count % 2 == 0) {
            count++;
        }

        EntityHitResult[] collisions = new EntityHitResult[count];
        int j = count / 2 + 1;
        float interval = angle / count - 1;

        for (int i = 0; i < j; i++) {
            collisions[j - 1 - i] = shootAtYRot(attacker, i * interval, radius);
            collisions[j - 1 + i] = shootAtYRot(attacker, -i * interval, radius);
        }

        collisions[j - 1] = shootAtYRot(attacker, 0, radius);
        HashSet<Entity> entities = new HashSet<>();

        for (EntityHitResult result : collisions) {
            if (result != null) {
                entities.add(result.getEntity());
            }
        }
        return entities;
    }

    public static EntityHitResult shootAtAngle(Entity attacker, float angle, float radius) {
        Vec3 eyePosition = attacker.getEyePosition();
        Vec3 vec3 = attacker.getUpVector(1.0F);
        Quaternionf quaternionf = (new Quaternionf()).setAngleAxis(angle * ((float) Math.PI / 180F), vec3.x, vec3.y, vec3.z);
        Vector3f mul = attacker.getViewVector(1.0F).toVector3f().rotate(quaternionf).mul(radius);
        Vector3f vector3f = eyePosition.toVector3f().add(mul);
        AABB aabb = attacker.getBoundingBox().expandTowards(new Vec3(mul)).inflate(1.0);
        testParticle(vector3f.x, vector3f.y, vector3f.z, attacker.level());
        return ProjectileUtil.getEntityHitResult(attacker, eyePosition, new Vec3(vector3f), aabb, PREDICATE, radius * radius);
    }

    private static Vec3 calculateViewVector(float pXRot, float pYRot) {
        float f = pXRot * (float) (Math.PI / 180.0);
        float f1 = -pYRot * (float) (Math.PI / 180.0);
        float f2 = Mth.cos(f1);
        float f3 = Mth.sin(f1);
        float f4 = Mth.cos(f);
        float f5 = Mth.sin(f);
        return new Vec3(f3 * f4, -f5, f2 * f4);
    }

    public static EntityHitResult shootAtYRot(Entity attacker, float yRot, float radius) {
        Vec3 position = attacker.position();
        float viewYRot = attacker.getYRot();
        Vec3 viewVector = calculateViewVector(0, viewYRot - yRot);
        Vec3 vec31 = viewVector.scale(radius);
        Vec3 end = position.add(vec31);
        AABB aabb = attacker.getBoundingBox().expandTowards(vec31).inflate(1.0);
        testParticle(end.x, end.y, end.z, attacker.level());
        return ProjectileUtil.getEntityHitResult(attacker, position, end, aabb, PREDICATE, radius * radius);
    }

    private static void testParticle(double x, double y, double z, Level level) {
        level.addParticle(
                ParticleTypes.DRAGON_BREATH,
                x,
                y,
                z,
                0,
                0,
                0
        );
    }
}
